home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 November
/
Macworld (1999-11).dmg
/
Updaters
/
WhiteCap 3.0.4
/
WhiteCap Source.sit
/
WhiteCap Source
/
Common
/
math
/
Expression.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-22
|
12KB
|
478 lines
#include "Expression.h"
#include "ExpressionDict.h"
#include "ExprVirtualMachine.h"
#if EG_WIN
#include "EgOSUtils.h"
#endif
#define __collapseTwo \
highestPriority = 0; \
for ( i = 0; i < numExps-1; i++ ) { \
priority = oper[ i ] & 0x0F00; \
if ( priority > highestPriority ) { \
highestPriority = priority; \
collapse = i; \
} \
} \
i = collapse; \
inVM.DoOp( expr[ i ], expr[ i + 1 ], oper[ i ] & 0xFF ); \
inVM.DeallocReg( expr[ i + 1 ] ); \
numExps--; \
oper[ i ] = oper[ i+1 ]; \
for ( i++; i < numExps; i++ ) { \
oper[ i ] = oper[ i+1 ]; \
expr[ i ] = expr[ i+1 ]; \
}
// Pre: All whitespace and illegal chars are removed
// Pre: Parens balence & all letters are capitalized
// Note: I normally don't write such huge, non-compartmentalized code, but speed is important
// Post: Allocates a register for the return value and returns that register
int Expression::Compile( char* inStr, long inLen, ExpressionDict& inDict, ExprVirtualMachine& inVM ) {
bool hasLetters = false;
float* floatPtr, val, **fcnPtr;
long highestPriority, priority, i, numExps = 0, startPos = 0;
long fcnCall, collapse, c, pos, parens = 0, firstParen = -1, size;
short oper[ 4 ];
short expr[ 4 ];
// Catch a negated expression. Make it the same as 0-...
if ( inLen > 0 && *inStr == '-' ) {
expr[ 0 ] = inVM.AllocReg();
oper[ 0 ] = 0x100 | '-';
inVM.Loadi( 0.0, expr[ 0 ] );
numExps = 1;
inStr++;
inLen--;
}
// Make a pass thru of the string, finding the starts and ends of all root exprs
for ( pos = 0; pos < inLen; pos++ ) {
c = inStr[ pos ];
if ( c == '(' ) {
if ( firstParen < 0 )
firstParen = pos;
parens++; }
else if ( c == ')' )
parens--;
// Don't consider any chars if we're not at the root level
if ( parens == 0 ) {
if ( c >= 'A' && c <= 'Z' )
hasLetters = true;
// Operators are what separate exprs
switch ( c ) {
case '-':
case '+': priority = 0x0100; goto doOp;
case '/':
case '%':
case '*': priority = 0x0200; goto doOp;
case '^': priority = 0x0300;
// Close cur expr
doOp: if ( pos > startPos && pos + 1 < inLen ) {
// Recurse
expr[ numExps ] = Compile( inStr + startPos, pos - startPos, inDict, inVM );
oper[ numExps ] = c | priority;
numExps++;
startPos = pos + 1;
// 4 exprs, given three priority operators, 4 exprs guaruntees we can collapse two into one expr
if ( numExps == 4 ) {
__collapseTwo
}
}
}
}
}
// Detect base case (ie, whole string is an expr)
if ( numExps == 0 ) {
// See if we found a fcn call or an unneeded paren pair: (...)
if ( firstParen >= 0 && firstParen <= 4 ) {
// Eval what's inside the parens
expr[ 0 ] = Compile( inStr + firstParen + 1, inLen - firstParen - 2, inDict, inVM );
// if we have a fcn (as opposed to just a paren pair)
if ( firstParen > 0 ) {
// Translate the string of the fcn to a (one byte) sub op code number
fcnCall = *((long*) inStr);
#if EG_WIN
fcnCall = EgOSUtils::RevBytes( fcnCall );
#endif
switch ( fcnCall ) {
case 'SQRT': fcnCall = cSQRT; break;
case 'ATAN': fcnCall = cATAN; break;
case 'ABS(': fcnCall = cABS; break;
case 'SIN(': fcnCall = cSIN; break;
case 'COS(': fcnCall = cCOS; break;
case 'TAN(': fcnCall = cTAN; break;
case 'LOG(': fcnCall = cLOG; break;
case 'EXP(': fcnCall = cEXP; break;
case 'SQR(': fcnCall = cSQR; break;
case 'SQWV': fcnCall = cSQWV; break;
case 'POS(': fcnCall = cPOS; break;
case 'RND(': fcnCall = cRND; break;
case 'SGN(': fcnCall = cSGN; break;
case 'TRWV': fcnCall = cTRWV; break;
case 'CLIP': fcnCall = cCLIP; break;
case 'SEED': fcnCall = cSEED; break;
default: {
fcnCall = 0;
UtilStr temp( inStr, firstParen );
fcnPtr = inDict.LookupFunc( temp, size );
inVM.UserFcnOp( expr[ 0 ], fcnPtr, size );
}
}
if ( fcnCall )
inVM.MathOp( expr[ 0 ], fcnCall );
} }
// Catch the case where we have a number/immediate
else if ( ! hasLetters ) {
val = UtilStr::GetFloatVal( inStr, inLen );
expr[ 0 ] = inVM.AllocReg();
inVM.Loadi( val, expr[ 0 ] ); }
// At this point we assume it's an identifier, so we'll look up its value
else {
UtilStr temp;
temp.Assign( inStr, inLen );
expr[ 0 ] = inVM.AllocReg();
floatPtr = inDict.LookupVar( temp );
if ( floatPtr )
inVM.Loadi( floatPtr, expr[ 0 ] );
} }
// If there more than one expression
else {
// Finish the current expr
if ( startPos < inLen ) {
expr[ numExps ] = Compile( inStr + startPos, inLen - startPos, inDict, inVM );
numExps++;
}
// When there'll be no more exprs, we're free to collapse all the exprs into one
while ( numExps > 1 ) {
__collapseTwo
}
}
return expr[ 0 ];
}
/*
#define __collapseTwo \
highestPriority = 0; \
for ( i = 0; i < numExps-1; i++ ) { \
priority = oper[ i ] & 0x0F00; \
if ( priority > highestPriority ) { \
highestPriority = priority; \
collapse = i; \
} \
} \
i = collapse; \
inVM.DoOp( i, oper[ i ] & 0xFF ); \
numExps--; \
oper[ i ] = oper[ i+1 ]; \
for ( i++; i < numExps; i++ ) { \
inVM.MoveUp( i ); \
oper[ i ] = oper[ i+1 ]; \
}
// Pre: All whitespace and illegal chars are removed
// Pre: Parens balence & all letters are capitalized
// Note: I normally don't write such huge, non-compartmentalized code, but speed is important
void Expression::Compile( char* inStr, long inLen, const Hashtable& inDict, ExprVirtualMachine& inVM ) {
bool hasLetters = false;
double* dblPtr, val;
long highestPriority, priority, i, numExps = 0, startPos = 0;
long fcnCall, collapse, c, pos, parens = 0, firstParen = -1;
short oper[ 4 ];
short reg[ 4 ];
// Catch a negated expression
if ( inLen > 0 && *inStr == '-' ) {
inVM.Loadi( 0.0 );
oper[ 0 ] = 0x100 | '-';
numExps = 1;
inStr++;
inLen--;
}
// Make a pass thru of the string, finding the starts and ends of all root exprs
for ( pos = 0; pos < inLen; pos++ ) {
c = inStr[ pos ];
if ( c == '(' ) {
if ( firstParen < 0 )
firstParen = pos;
parens++; }
else if ( c == ')' )
parens--;
// Don't consider any chars if we're not at the root level
if ( parens == 0 ) {
if ( c >= 'A' && c <= 'Z' )
hasLetters = true;
// Operators are what separate exprs
switch ( c ) {
case '-':
case '+': priority = 0x0100; goto doOp;
case '/':
case '%':
case '*': priority = 0x0200; goto doOp;
case '^': priority = 0x0300;
// Close cur expr
doOp: if ( pos > startPos && pos + 1 < inLen ) {
// Push registers 0 thru (numExps-1), the registers in use
inVM.MassPush( numExps - 1 );
// Recurse
Compile( inStr + startPos, pos - startPos, inDict, inVM );
oper[ numExps ] = c | priority;
numExps++;
startPos = pos + 1;
// The return value is in reg 0. Also pop/restore the FP registers in use
inVM.Move( numExps - 1 );
inVM.MassPop( numExps - 2 );
// 4 exprs, given three priority operators, 4 exprs guaruntees we can collapse two into one expr
if ( numExps == 4 ) {
__collapseTwo
}
}
}
}
}
// Detect base case (ie, whole string is an expr)
if ( numExps == 0 ) {
// See if we found a fcn call or an unneeded paren pair: (...)
if ( firstParen >= 0 && firstParen <= 4 ) {
// Eval what's inside the parens
Compile( inStr + firstParen + 1, inLen - firstParen - 2, inDict, inVM );
// Translate the string of the fcn to a (one byte) code number
fcnCall = *((long*) inStr);
switch ( fcnCall ) {
#if EG_MAC
case 'SQRT': fcnCall = cSQRT; break;
case 'ATAN': fcnCall = cATAN; break;
case 'ABS(': fcnCall = cABS; break;
case 'SIN(': fcnCall = cSIN; break;
case 'COS(': fcnCall = cCOS; break;
case 'TAN(': fcnCall = cTAN; break;
case 'LOG(': fcnCall = cLOG; break;
case 'EXP(': fcnCall = cEXP; break;
case 'SQR(': fcnCall = cSQR; break;
case 'SQWV': fcnCall = cSQWV; break;
case 'POS(': fcnCall = cPOS; break;
case 'RND(': fcnCall = cRND; break;
#elif EG_WIN
case 'TRQS': fcnCall = cSQRT; break;
case 'NATA': fcnCall = cATAN; break;
case '(SBA': fcnCall = cABS; break;
case '(NIS': fcnCall = cSIN; break;
case '(SOC': fcnCall = cCOS; break;
case '(NAT': fcnCall = cTAN; break;
case '(GOL': fcnCall = cLOG; break;
case '(PXE': fcnCall = cEXP; break;
case '(RQS': fcnCall = cSQR; break;
case 'VWQS': fcnCall = cSQWV; break;
case '(SOP': fcnCall = cPOS; break;
case '(DNR': fcnCall = cRND; break;
#endif
default: fcnCall = 0; break;
}
inVM.MathOp( fcnCall ); }
// Catch the case where we have a number/immediate
else if ( ! hasLetters ) {
val = UtilStr::GetFloatVal( inStr, inLen );
inVM.Loadi( val ); }
// At this point we assume it's an identifier, so we'll look up its value
else {
UtilStr temp;
temp.Assign( inStr, inLen );
if ( inDict.Get( &temp, &dblPtr ) )
inVM.Loadi( dblPtr );
} }
// If there more than one expression
else {
// Push registers 0 thru (numExps-1), the registers in use
inVM.MassPush( numExps - 1 );
// Finish the current expr
if ( startPos < inLen ) {
Compile( inStr + startPos, inLen - startPos, inDict, inVM );
numExps++;
}
// The return value is in reg 0. Also pop/restore the FP registers in use
inVM.Move( numExps - 1 );
inVM.MassPop( numExps - 2 );
// When there'll be no more exprs, we're free to collapse all the exprs into one
while ( numExps > 1 ) {
__collapseTwo
}
}
// Register 0 is the return value by convention
}
*/
bool Expression::IsDependent( char* inStr ) {
long pos, len = 0, c;
while ( inStr[ len ] )
len++;
pos = mEquation.contains( inStr, len, 0, false );
while ( pos > 0 ) {
// This expr is depndent on inStr when we find a substring match that *isn't* a substring of another identifier
c = mEquation.getChar( pos - 1 );
if ( c < 'A' || c > 'Z' ) {
c = mEquation.getChar( pos + len );
if ( c < 'A' || c > 'Z' )
return true;
}
pos = mEquation.contains( inStr, len, pos, false );
}
return false;
}
bool Expression::Weight( Expression& inExpr, float* inTransitionLink ) {
if ( mIsCompiled && inExpr.mIsCompiled ) {
Chain( inExpr, inTransitionLink );
return true; }
else
return false;
}
void Expression::Assign( Expression& inExpr ) {
mEquation.Assign( inExpr.mEquation );
mIsCompiled = inExpr.mIsCompiled;
if ( inExpr.mIsCompiled )
ExprVirtualMachine::Assign( inExpr );
}
bool Expression::Compile( const UtilStr& inStr, ExpressionDict& inDict ) {
int i, c, parens, len;
mEquation.Assign( inStr );
// Case insensitive, remove all spaces
mEquation.Capitalize();
mEquation.Remove( " " );
mEquation.Remove( "\t" );
// Check for balenced parens
parens = 0;
len = mEquation.length();
for ( i = 1; i <= len && parens >= 0; i++ ) {
c = mEquation.getChar( i );
switch ( c ) {
case '(': parens++; break;
case ')': parens--; break;
}
}
if ( parens != 0 ) {
mEquation.Wipe();
mIsCompiled = false; }
else {
// Wipe whatever program was in the VM before
Clear();
if ( len > 0 ) {
// Generate insts for the VM that evaluate mEquation
int retRegNum = Compile( mEquation.getCStr(), mEquation.length(), inDict, *this );
Move( retRegNum, 0 ); }
else {
// Color register 0 as used
AllocReg();
// If the expression is blank, make it return 0
Loadi( 0.0, 0 );
}
PrepForExecution();
mIsCompiled = true;
}
return mIsCompiled;
}